// -*- C++ -*-
/* $Id: Sprite.h 1.1 1998/04/23 17:46:25 atterer Exp $
  __   _
  |_) /|  Copyright Richard Atterer
  | \/|  <atterer@informatik.tu-muenchen.de>
   ` 
  Sprite and Spritearea classes.
  OSLib needed.

  Sprite files are held in memory in their original layout, to allow the
  OS_SpriteOp routines to operate on them. To access actual sprite or sprite
  area data, use object.raw()

  When returning errors like memory allocation failed, returns os_error
  struct with user error numbers (bit 30 set). If a constructor fails, don't
  forget to delete the object.

  $Log: Sprite.h $
  Revision 1.1  1998/04/23 17:46:25  atterer
  Initial revision

*/
#ifndef _Sprite_h
#define _Sprite_h
#include <osspriteop.h>
#include <string.h>

// you can alter the base number for internal error numbers
#ifndef Sprite_ErrorBase
#define Sprite_ErrorBase 0x40000000
#endif

typedef unsigned char  byte;
typedef unsigned int   word;
typedef unsigned short hword;

class Sprite;
class SpritePixel;

class Spritearea {
  friend class Sprite;
public:
  // extsize is size of extension area (usually null)
  Spritearea(os_error*& error, int size = 16, int extsize = 0);
  osspriteop_area& raw() { return *data; }
  // reserves (file_length + 20 + size) bytes for the area and loads file:
  Spritearea(os_error*& error, const char* filename, int size = 0);
  ~Spritearea();
  Sprite& first() const { return *firstptr; }
  /* Mainly internal use: Update names/pointers to sprites after OS_SpriteOp.
     ptrs to sprite *objects* stay valid even if sprites in area shuffle */
  void rescan(os_error*& error);
protected:
  osspriteop_area* data; // pointer to actual sprite area data
  Sprite* firstptr; // ptr to object for 1st sprite or NULL if empty area
};

class Sprite {
  friend class Spritearea;
  // SpritePixel::init only accesses modebpp[]
  friend class SpritePixel;
public:
  static const int errnum_nomem = Sprite_ErrorBase + 0;
  static const struct os_error error_nomem;
  ~Sprite();
  Sprite& prev() const { return *prevptr; }
  Sprite& next() const { return *nextptr; }
  Spritearea& area() const { return *areaptr; }
  const char* name() const { return sprname; }
  osspriteop_header& raw() const { return *data; }
  osspriteop_area& rawarea() const { return *rawareaptr; }
  int height() const { return data->height + 1; }
  int width() const {
    int w;
    xosspriteop_read_sprite_size(osspriteop_PTR, rawareaptr,
        static_cast<osspriteop_id>(data), &w, SKIP, SKIP, SKIP);
    return w;
  }
  // bpp() is 0...5 for 1bpp to 32bpp
  int bpp() const {
    unsigned modeword = unsigned(data->mode);
    if (modeword & 0xf8000000)
      return int(modeword >> 27) - 1; // new format
    else // old format
      return int(modebpp[modeword / 4] >> (modeword & 3) * 2) & 3;
  }
  // compare sprite name with string, taking care of unterminated name
  static bool samename(osspriteop_header* rawsprite, const char* name);
protected:
  osspriteop_header* data;
  Spritearea* areaptr;
  osspriteop_area* rawareaptr;
  Sprite* prevptr; // doubly linked list - prev/next is NULL at start/end
  Sprite* nextptr;
  /* Additional name field allows objects to remain the same (and at the
     same address) even if area shuffles after a merge etc. Also allows
     name to be return as string if it is 12 chars long. */
  char sprname[13];
  /* NB in contrast to other constructors, does NOT create a new sprite in
     the sprite area - only a new *object* */
  Sprite(osspriteop_header* sprdata, Spritearea* area, Sprite* prev = 0,
      Sprite* next = 0) : data(sprdata), areaptr(area), prevptr(prev),
      nextptr(next) {
    rawareaptr = areaptr->data;
    strncpy(sprname, sprdata->name, 12);
    sprname[12] = 0;
    return;
  };
  static const byte modebpp[];
};

#endif
